package com.gwnet.user.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gwnet.common.bean.vo.LoginVO;
import com.gwnet.common.core.enums.UserType;
import com.gwnet.common.core.exception.base.BaseException;
import com.gwnet.common.enums.SendTypeEnum;
import com.gwnet.common.redis.utils.RedisUtils;
import com.gwnet.common.satoken.utils.LoginHelper;
import com.gwnet.common.util.IPHelper;
import com.gwnet.common.util.UserUtils;
import com.gwnet.system.api.model.LoginUser;
import com.gwnet.user.domain.SmsLog;
import com.gwnet.user.domain.CusUser;
import com.gwnet.user.domain.bo.UserLoginBO;
import com.gwnet.user.mapper.SmsLogMapper;
import com.gwnet.user.mapper.CusUserMapper;
import com.gwnet.user.service.CusUserService;
import com.gwnet.user.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * @author: WangLiang
 * @date: 2024/7/20 10:44
 */
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {

    private final SmsLogMapper smsLogMapper;
    private final CusUserMapper userMapper;
    private final CusUserService userService;

    /**
     * 短信验证码的前缀
     */
    private static final String CHECK_VALID_CODE_LOCK_PREFIX = "checkValidLock_";

    /**
     * 短信验证码的前缀
     */
    private static final String CHECK_VALID_CODE_NUM_PREFIX = "checkValidCodeNum_";

    /**
     * 一段时间内短信验证码的最大验证次数
     */
    private static final int TIMES_CHECK_VALID_CODE_NUM = 10;

    /**
     * 当天最大验证码短信发送量
     */
    private static final int TODAY_MAX_SEND_VALID_SMS_NUMBER = 10;

    /**
     * 短信列表的大小/列表的索引
     */
    private static final Integer INDEX = 0;



    @Autowired
    public LoginServiceImpl(SmsLogMapper smsLogMapper, CusUserMapper userMapper, CusUserService userService) {
        this.smsLogMapper = smsLogMapper;
        this.userMapper = userMapper;
        this.userService = userService;
    }


    @Lock4j(keys = {"#phone"})
    @Override
    public Boolean sendSms(String phone) {
        SendTypeEnum sendTypeEnum = SendTypeEnum.REGISTER;

        // 校验是否符合发送条件
        boolean valid = checkCanSend(sendTypeEnum,phone);
        if(valid){
            // 发送短信
            SmsResponse smsResponse = send(sendTypeEnum,phone);
            return smsResponse.isSuccess();
        }
        return false;
    }

    /**
     * 检查是否符合发送条件
     *  1.今日已发送最大上限
     *  2.一分钟只能发送一次验证码
     * @param sendTypeEnum 发送类型
     * @param phone
     * @return
     */
    private boolean checkCanSend(SendTypeEnum sendTypeEnum, String phone){
        // 保存验证码发送记录到短信表，验证是否达到当天最大发送数量，达到则今日不允许再次发送,防止盗刷
        LambdaQueryWrapper<SmsLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.gt(SmsLog::getRecDate, DateUtil.beginOfDay(new Date()))
                .lt(SmsLog::getRecDate, DateUtil.endOfDay(new Date()))
                .eq(SmsLog::getType, sendTypeEnum.getValue())
                .orderByDesc(SmsLog::getRecDate);
        List<SmsLog> smsLogList = smsLogMapper.selectList(queryWrapper);
        if (smsLogList.size() >= TODAY_MAX_SEND_VALID_SMS_NUMBER) {
            // 今日发送短信验证码次数已达到上限
            throw new BaseException("今日发送短信验证码次数已达到上限");
        }

        if (smsLogList.size() > INDEX){
            SmsLog smsLogLast = smsLogList.get(INDEX);
            long currentTimeMillis = System.currentTimeMillis();
            long timeDb = DateUtil.offsetSecond(smsLogLast.getRecDate(), 60).getTime();
            if (currentTimeMillis < timeDb){
                // 一分钟内只能发送一次验证码
                throw new BaseException("一分钟内只能发送一次验证码");
            }
            // 将上一条验证码失效
            smsLogMapper.invalidSmsByMobileAndType(phone, sendTypeEnum.getValue());
            return true;
        }
        return true;
    }





    @Override
    public LoginVO smsRegOrLogin(UserLoginBO userLoginBO) {

        boolean validCode = checkValidCode(userLoginBO.getPhone(),userLoginBO.getCode(), SendTypeEnum.REGISTER);

        if(!validCode){
            // 所属模块，错误码，错误码对应的参数，错误消息
            throw new BaseException("验证码有误或已过期");
        }

        CusUser userByPhone = getUserByPhone(userLoginBO.getPhone());

        if(ObjectUtils.isNotEmpty(userByPhone)){
            // 1.有效 0.无效 该用户被禁用
            if(userByPhone.getStatus().equals(0)){
                // 该用户已经被禁止登录
                throw new BaseException("该用户已经被禁止登录");
            }
            return buildLoginVO(userByPhone);
        }

        // 绑定手机号
        LambdaQueryWrapper<CusUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CusUser::getUserId,userLoginBO.getUserId());
        List<CusUser> cusUsers = userMapper.selectList(queryWrapper);
        if(cusUsers.size() > 0){
            CusUser cusUser = cusUsers.get(0);
            // 随机昵称
            cusUser.setNickName(UserUtils.generateRandomNickname());
            // 随机头像
            cusUser.setPic("");
            // 手机号码
            cusUser.setUserPhone(userLoginBO.getPhone());
            // 注册时间
            cusUser.setUserRegtime(new Date());
            // 注册IP
            cusUser.setUserRegip(IPHelper.getIpAddr());
            // 是否有效 1.有效 0.无效
            cusUser.setStatus(1);
            boolean status = userService.saveOrUpdate(cusUser);

            LoginVO loginVO = buildLoginVO(cusUser);
            return loginVO;
        }

        // 如果该用户没有通过code码获取并保存过openID，应该先根据Code码获取并保存一下openId
        return null;
    }

    @Override
    public CusUser getUserByPhone(String phone){

        LambdaQueryWrapper<CusUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CusUser::getUserPhone,phone);
        List<CusUser> userList = userMapper.selectList(queryWrapper);
        if(userList.size() > 0){
            CusUser cusUser = userList.get(0);
            return cusUser;
        }
        return null;
    }

    private LoginUser buildLoginUser(CusUser cusUser){
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(cusUser.getUserId());
        loginUser.setNickname(cusUser.getNickName());
        loginUser.setUserType(UserType.APP_USER.getUserType());
        return loginUser;
    }

    @Override
    public LoginVO buildLoginVO(CusUser cusUser){
        // 生成token
        LoginUser loginUser = buildLoginUser(cusUser);
        SaLoginModel model = new SaLoginModel();
        LoginHelper.login(loginUser,model);

        LoginVO loginVO = new LoginVO();
        loginVO.setAccessToken(StpUtil.getTokenValue());
        loginVO.setExpireIn(StpUtil.getTokenTimeout());
        loginVO.setOpenid(cusUser.getBizUserId());
        loginVO.setUserId(cusUser.getUserId());
        return loginVO;
    }


    /**
     * 验证前校验
     * @param phone
     * @param code
     * @param sendTypeEnum
     * @return
     */
    public boolean checkValidCode(String phone, String code, SendTypeEnum sendTypeEnum) {
        long checkValidCodeNum = RedisUtils.incrAtomicValue(CHECK_VALID_CODE_NUM_PREFIX + phone);
//        if (checkValidCodeNum == 0) {
        // 半小时后失效
        RedisUtils.expire(CHECK_VALID_CODE_NUM_PREFIX + phone, 1800);
//        }
        if (checkValidCodeNum >= TIMES_CHECK_VALID_CODE_NUM) {
            // 验证码校验过频繁，请稍后再试
            throw new BaseException("验证码校验过频繁，请稍后再试");
        }

        SmsLog dbSms = smsLogMapper.selectOne(new LambdaQueryWrapper<SmsLog>()
                .eq(SmsLog::getUserPhone, phone)
                .eq(SmsLog::getMobileCode, code)
                .eq(SmsLog::getStatus, 1)
                .eq(SmsLog::getType, sendTypeEnum.getValue()));
        // 没有找到当前的验证码
        if (dbSms == null) {
            RedisUtils.decrAtomicValue(CHECK_VALID_CODE_NUM_PREFIX + phone);
            return false;
        }
        RedisUtils.deleteObject(CHECK_VALID_CODE_NUM_PREFIX + phone);
        // 标记为失效状态
        dbSms.setStatus(0);
        smsLogMapper.updateById(dbSms);
        // 验证码已过期
        DateTime offsetMinute = DateUtil.offsetMinute(dbSms.getRecDate(), 5);
        if (offsetMinute.getTime() < System.currentTimeMillis()) {
            RedisUtils.incrAtomicValue(CHECK_VALID_CODE_NUM_PREFIX + phone);
            return false;
        }

        return true;

    }

    /**
     * 发送短信
     * @param phone
     * @return
     */
    private SmsResponse send(SendTypeEnum sendTypeEnum, String phone){

        // 验证码
        String code = RandomUtil.randomNumbers(6);

        // 保存记录到用户短信表
        SmsLog smsLog = new SmsLog();
        smsLog.setType(sendTypeEnum.getValue());
        smsLog.setMobileCode(code);
        smsLog.setRecDate(new Date());
        smsLog.setStatus(1);
        // 通过封装的 StpUtil.getLoginId(); 获取用户ID
//        smsLog.setUserId(userId);
        smsLog.setUserPhone(phone);
        // 短信内容要同步发送平台的内容，
        smsLog.setContent(code);
        smsLogMapper.insert(smsLog);

        String templateId = "模板ID";
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);

        map.put("code", code);
        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
        SmsResponse smsResponse = smsBlend.sendMessage(phone, templateId, map);
        return smsResponse;
    }

//    /**
//     * 获取锁的电话号码
//     * @param phone
//     * @return
//     */
//    public String getCheckCodePhone(String phone) {
//        return CHECK_VALID_CODE_LOCK_PREFIX + phone;
//    }
}
